// Copyright (C) 2010 Félix Cloutier.

// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License 2.0 for more details.

// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/

#include "JitExecutor.h"
#include "../PowerPC.h"
#include "../../CoreTiming.h"

namespace LLVMJit
{

// totally arbitrary number of cycles, tune as required
const u32 JitExecutor::eventLoopCycleTreshold = 0x400;

struct CoroutineCall
{
    void (*starter)(Coroutine*, JitExecutor*);
    Coroutine* coroutine;
    JitExecutor* executor;
    
    static void Start(CoroutineCall* arguments)
    {
        arguments->starter(arguments->coroutine, arguments->executor);
    }
};

void JitExecutor::CoroutineStarter(Coroutine* coroutine, JitExecutor* executor)
{
	executor->elapsedCycles += executor->RunStub(coroutine, NPC);
	coroutine->Yield();
}

void JitExecutor::SetExpectedNPC(u32 expectedNPC)
{
	this->expectedNPC = expectedNPC;
}

u32 JitExecutor::RunStub(Coroutine* coroutine, u32 address)
{
	llvm::Function* llvmFunction = recompiler->GetFunction(address);
	RecompiledFunction executable = GetCompiled(llvmFunction);
	return executable(this, coroutine, &PowerPC::ppcState);
}

void JitExecutor::Break(Coroutine* coroutine, u32 expectedNPC)
{
	SetExpectedNPC(expectedNPC);
	coroutine->Yield();
}

JitExecutor::JitExecutor(llvm::ExecutionEngine* engine, JitRecompiler* recompiler)
{
	this->recompiler = recompiler;
	this->engine = engine;
	
	bindings = new JitBindings(engine, recompiler->GetModule());
	coroutine = Coroutine::Create();
	starter = new LLVMJit::CoroutineCall;
	
	ResetCyclesCount();
	CoreTiming::slicelength = eventLoopCycleTreshold;
}

JitExecutor::RecompiledFunction JitExecutor::Compile(llvm::Function* function)
{
	void* result = engine->getPointerToFunction(function);
	return reinterpret_cast<RecompiledFunction>(result);
}

JitExecutor::RecompiledFunction JitExecutor::GetCompiled(llvm::Function* function)
{
	if (compiledCache.count(function) == 0)
		compiledCache[function] = Compile(function);
	return compiledCache[function];
}

JitBindings* JitExecutor::GetBindings()
{
	return bindings;
}

void JitExecutor::Resume()
{
	if (NPC != expectedNPC)
	{
		starter->starter = CoroutineStarter;
		starter->coroutine = coroutine;
		starter->executor = this;
		Coroutine::EntryPoint entryPoint =
			reinterpret_cast<Coroutine::EntryPoint>(CoroutineCall::Start);
		coroutine->SetEntryPoint(entryPoint, starter);
	}
	coroutine->Yield();
	
	while (elapsedCycles > eventLoopCycleTreshold)
	{
		elapsedCycles -= eventLoopCycleTreshold;
		CoreTiming::downcount = 0;
		CoreTiming::Advance();
		
		if (PowerPC::ppcState.Exceptions)
			PowerPC::CheckExceptions();
	}
}

void JitExecutor::ClearCache()
{
	typedef std::map<llvm::Function*, RecompiledFunction>::iterator
		map_iterator;
	
	for (map_iterator iter = compiledCache.begin(); iter != compiledCache.end(); iter++)
		engine->freeMachineCodeForFunction(iter->first);
	
	compiledCache.clear();
}

void JitExecutor::ClearCache(u32 address)
{
	llvm::Function* function = recompiler->GetCachedFunction(address);
	if (function == NULL) return;
	
	engine->freeMachineCodeForFunction(function);
	recompiler->ClearCache(address);
}

u32 JitExecutor::GetCyclesCount()
{
	return elapsedCycles;
}

void JitExecutor::ResetCyclesCount()
{
	elapsedCycles = 0;
}

JitExecutor::~JitExecutor()
{
	ClearCache();
	delete bindings;
	delete engine;
	delete coroutine;
	delete starter;
}

}
